package com.jacky.webflux.jwt.security;

import com.jacky.webflux.jwt.token.JwtProperties;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.server.authentication.ServerAuthenticationConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.Objects;

@Component
public class JwtServerAuthenticationConverter implements ServerAuthenticationConverter {

    private final static Logger LOGGER = LoggerFactory.getLogger(JwtServerAuthenticationConverter.class);

    private final JwtProperties jwtProperties;

    public JwtServerAuthenticationConverter(JwtProperties jwtProperties) {
        this.jwtProperties = jwtProperties;
    }

    @Override
    public Mono<Authentication> convert(ServerWebExchange exchange) {
        String reqPath = exchange.getRequest().getPath().value();
        if (reqPath.contains("/auth/login")) {
            return Mono.empty();
        }

        return Mono.justOrEmpty(exchange)
                .flatMap(it -> {
                    List<String> authHeaders = it.getRequest().getHeaders().get(HttpHeaders.AUTHORIZATION);
                    if (Objects.isNull(authHeaders)) {
                        return Mono.error(new BadCredentialsException("there is not AUTHORIZATION info found"));
                    }
                    return Mono.just(authHeaders);
                }).filter(it -> {
                    if (it.isEmpty())
                        throw new BadCredentialsException("there is not token found");
                    return true;
                }).log()
                .map(it -> it.get(0))
                .filter(it -> {
                    if (!it.startsWith(jwtProperties.getTokenPrefix()))
                        throw new BadCredentialsException("unrecognized token prefix");
                    return true;
                }).map(token -> {
                    Authentication auth = new UsernamePasswordAuthenticationToken(token, token);
                    SecurityContextHolder.getContext().setAuthentication(auth);
                    return auth;
                });
    }
}
